package edu.northwestern.cbits.purple_robot_manager.probes.services; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.net.Uri; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; import android.widget.Toast; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.Map; import edu.northwestern.cbits.purple_robot_manager.EncryptionManager; import edu.northwestern.cbits.purple_robot_manager.R; import edu.northwestern.cbits.purple_robot_manager.logging.LogManager; import edu.northwestern.cbits.purple_robot_manager.logging.SanityCheck; import edu.northwestern.cbits.purple_robot_manager.logging.SanityManager; import edu.northwestern.cbits.purple_robot_manager.probes.Probe; import edu.northwestern.cbits.xsi.oauth.JawboneApi; import edu.northwestern.cbits.xsi.oauth.Keystore; import edu.northwestern.cbits.xsi.oauth.OAuthActivity; public class JawboneProbe extends Probe { public final static String PROBE_NAME = "edu.northwestern.cbits.purple_robot_manager.probes.services.JawboneProbe"; private static final String ENABLED = "config_feature_jawbone_probe_enabled"; private static final boolean DEFAULT_ENABLED = false; private long _lastUpdate = 0; @Override public String getPreferenceKey() { return "services_jawbone"; } @Override public String summary(Context context) { return context.getString(R.string.summary_jawbone_probe_desc); } @Override public String probeCategory(Context context) { return context.getResources().getString(R.string.probe_external_services_category); } @Override public String name(Context context) { return "edu.northwestern.cbits.purple_robot_manager.probes.services.JawboneProbe"; } @Override public String title(Context context) { return context.getString(R.string.title_jawbone_probe); } @Override public void enable(Context context) { SharedPreferences prefs = Probe.getPreferences(context); Editor e = prefs.edit(); e.putBoolean(JawboneProbe.ENABLED, true); e.commit(); } @Override public void disable(Context context) { SharedPreferences prefs = Probe.getPreferences(context); Editor e = prefs.edit(); e.putBoolean(JawboneProbe.ENABLED, false); e.commit(); } private void initKeystore(Context context) { Keystore.put(JawboneApi.CONSUMER_KEY, context.getString(R.string.jawbone_consumer_key)); Keystore.put(JawboneApi.CONSUMER_SECRET, context.getString(R.string.jawbone_consumer_secret)); } @Override public boolean isEnabled(final Context context) { final SharedPreferences prefs = Probe.getPreferences(context); if (super.isEnabled(context)) { if (prefs.getBoolean(JawboneProbe.ENABLED, false)) { this.initKeystore(context); String token = prefs.getString("oauth_jawbone_token", ""); final String title = context.getString(R.string.title_jawbone_check); final SanityManager sanity = SanityManager.getInstance(context); final JawboneProbe me = this; final long now = System.currentTimeMillis(); if (token == null || token.trim().length() == 0) { String message = context.getString(R.string.message_jawbone_check); Runnable action = new Runnable() { @Override public void run() { me.fetchAuth(context); } }; sanity.addAlert(SanityCheck.WARNING, title, message, action); } else { Keystore.put(JawboneApi.USER_TOKEN, token); sanity.clearAlert(title); if (now - this._lastUpdate > 1000 * 60 * 15) // 15 min refresh interval { this._lastUpdate = now; Runnable r = new Runnable() { public void run() { try { JSONObject goals = JawboneApi.fetch(context, Uri.parse("https://jawbone.com/nudge/api/v.1.1/users/@me/goals"), "Purple Robot"); Bundle bundle = new Bundle(); bundle.putString("PROBE", me.name(context)); bundle.putLong("TIMESTAMP", System.currentTimeMillis() / 1000); JSONObject data = goals.getJSONObject("data"); JSONObject remaining = data.getJSONObject("remaining_for_day"); if (data.has("move_steps")) { double goalSteps = data.getDouble("move_steps"); double remainingSteps = remaining.getDouble("move_steps_remaining"); bundle.putDouble("STEPS_GOAL", goalSteps); bundle.putDouble("GOAL_STEPS", goalSteps - remainingSteps); } if (data.has("sleep_total")) { double goalSleep = data.getDouble("sleep_total"); double remainingSleep = remaining.getDouble("sleep_seconds_remaining"); bundle.putDouble("SLEEP_GOAL", goalSleep); bundle.putDouble("GOAL_SLEEP", goalSleep - remainingSleep); } if (remaining.has("intake_calories_remaining")) bundle.putDouble("CALORIES_REMAINING", remaining.getDouble("intake_calories_remaining")); JSONObject mood = JawboneApi.fetch(context, Uri.parse("https://jawbone.com/nudge/api/v.1.1/users/@me/mood"), "Purple Robot"); data = mood.getJSONObject("data"); if (data.has("sub_type")) { bundle.putString("MOOD_TITLE", data.getString("title")); bundle.putDouble("MOOD_TIMESTAMP", data.getDouble("time_updated")); switch(data.getInt("sub_type")) { case 1: bundle.putDouble("MOOD", 8.0); break; case 2: bundle.putDouble("MOOD", 7.0); break; case 3: bundle.putDouble("MOOD", 6.0); break; case 8: bundle.putDouble("MOOD", 5.0); break; case 4: bundle.putDouble("MOOD", 4.0); break; case 5: bundle.putDouble("MOOD", 3.0); break; case 6: bundle.putDouble("MOOD", 2.0); break; case 7: bundle.putDouble("MOOD", 1.0); break; } } JSONObject moves = JawboneApi.fetch(context, Uri.parse("https://jawbone.com/nudge/api/v.1.1/users/@me/noves"), "Purple Robot"); data = moves.getJSONObject("data"); if (moves.has("items")) { JSONObject item = data.getJSONArray("items").getJSONObject(0); bundle.putDouble("DISTANCE", item.getDouble("distance")); bundle.putDouble("STEPS", item.getDouble("steps")); bundle.putDouble("ACTIVE_TIME", item.getDouble("active_time")); bundle.putDouble("LONGEST_ACTIVE", item.getDouble("longest_active")); bundle.putDouble("INACTIVE_TIME", item.getDouble("inactive_time")); bundle.putDouble("LONGEST_IDLE", item.getDouble("longest_idle")); bundle.putDouble("CALORIES", item.getDouble("calories")); bundle.putDouble("BMR_DAY", item.getDouble("bmr_day")); bundle.putDouble("WO_CALORIES", item.getDouble("wo_calories")); bundle.putDouble("WO_TIME", item.getDouble("wo_time")); bundle.putDouble("WO_ACTIVE_TIME", item.getDouble("wo_active_time")); bundle.putDouble("WO_COUNT", item.getDouble("wo_count")); } me.transmitData(context, bundle); } catch (Exception e) { e.printStackTrace(); } } }; Thread t = new Thread(r); t.start(); } } return true; } } return false; } private void fetchAuth(Context context) { String userId = EncryptionManager.getInstance().getUserHash(context); Intent intent = new Intent(context, OAuthActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(OAuthActivity.CONSUMER_KEY, context.getString(R.string.jawbone_consumer_key)); intent.putExtra(OAuthActivity.CONSUMER_SECRET, context.getString(R.string.jawbone_consumer_secret)); intent.putExtra(OAuthActivity.REQUESTER, "jawbone"); intent.putExtra(OAuthActivity.CALLBACK_URL, "http://tech.cbits.northwestern.edu/oauth/jawbone"); intent.putExtra(OAuthActivity.LOG_URL, LogManager.getInstance(context).getLogUrl(context)); intent.putExtra(OAuthActivity.HASH_SECRET, userId); context.startActivity(intent); } @Override public Map<String, Object> configuration(Context context) { Map<String, Object> map = super.configuration(context); // SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); return map; } @Override public void updateFromMap(Context context, Map<String, Object> params) { super.updateFromMap(context, params); } @Override public PreferenceScreen preferenceScreen(final Context context, PreferenceManager manager) { final PreferenceScreen screen = super.preferenceScreen(context, manager); screen.setTitle(this.title(context)); screen.setSummary(this.summary(context)); CheckBoxPreference enabled = new CheckBoxPreference(context); enabled.setTitle(R.string.title_enable_probe); enabled.setKey(JawboneProbe.ENABLED); enabled.setDefaultValue(JawboneProbe.DEFAULT_ENABLED); screen.addPreference(enabled); final SharedPreferences prefs = Probe.getPreferences(context); String token = prefs.getString("oauth_jawbone_token", null); String secret = prefs.getString("oauth_jawbone_secret", null); final Preference authPreference = new Preference(context); authPreference.setTitle(R.string.title_authenticate_jawbone_probe); authPreference.setSummary(R.string.summary_authenticate_jawbone_probe); final Preference logoutPreference = new Preference(context); logoutPreference.setTitle(R.string.title_logout_jawbone_probe); logoutPreference.setSummary(R.string.summary_logout_jawbone_probe); final JawboneProbe me = this; authPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { me.fetchAuth(context); screen.addPreference(logoutPreference); screen.removePreference(authPreference); return true; } }); logoutPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { Editor e = prefs.edit(); e.remove("oauth_jawbone_token"); e.remove("oauth_jawbone_secret"); e.commit(); me._lastUpdate = 0; screen.addPreference(authPreference); screen.removePreference(logoutPreference); if (context instanceof Activity) { Activity activity = (Activity) context; activity.runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(context, context.getString(R.string.toast_jawbone_logout), Toast.LENGTH_LONG).show(); } }); } return true; } }); if (token == null || secret == null) screen.addPreference(authPreference); else screen.addPreference(logoutPreference); return screen; } @Override public String summarizeValue(Context context, Bundle bundle) { double steps = bundle.getDouble("STEPS_GOAL"); double progress = (bundle.getDouble("GOAL_STEPS") / steps) * 100; return String.format(context.getResources().getString(R.string.summary_jawbone), steps, progress); } }